﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading;
using Library.Collections;
using Library.Collections.Distributed;
using Library.Collections.Specialized;
using Library.Linq;
using Library.Linq.Distributed;
using INDEX = System.UInt64;

namespace Library.Distributed
{
    public abstract class Context : /*System.Runtime.Remoting.Contexts.Context,*/ ICountable, Library.Collections.Generic.IVectorMultidimensionalReadOnly<Node>, INotifyCollectionChanging<Node>, INotifyCollectionChanged<Node>, IIsReadOnly, IDisposable
    {
        public static Context Current
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        public abstract INDEX[] Index { get; }

        public abstract Node this[params INDEX[] index] { get; }

        public abstract bool Contains(Node node);

        public abstract bool Contains(Node item, out INDEX[] index);

        public abstract bool IsReadOnly { get; }

        public abstract INDEX Count { get; }

        public abstract INDEX[] Extent { get; }

        public abstract INDEX Dimensions { get; }

        public abstract INDEX GetLowerBound(INDEX dimension);

        public abstract INDEX GetUpperBound(INDEX dimension);

        //public abstract bool Add(Node item);

        //public abstract bool Remove(Node item);

        protected void OnNodeAdding(Node item)
        {
            if (CollectionChanging != null)
            {
                CollectionChanging(this, new NotifyCollectionChangedEventArgs<Node>(NotifyCollectionChangedAction.Add, item));
            }
        }
        protected void OnNodeAdded(Node item)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs<Node>(NotifyCollectionChangedAction.Add, item));
            }
        }
        protected void OnNodeRemoving(Node item)
        {
            if (CollectionChanging != null)
            {
                CollectionChanging(this, new NotifyCollectionChangedEventArgs<Node>(NotifyCollectionChangedAction.Remove, item));
            }
        }
        protected void OnNodeRemoved(Node item)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs<Node>(NotifyCollectionChangedAction.Remove, item));
            }
        }

        public event NotifyCollectionChangingEventHandler<Node> CollectionChanging;
        event NotifyCollectionChangingEventHandler INotifyCollectionChanging.CollectionChanging
        {
            add
            {
                CollectionChanging += new NotifyCollectionChangingEventHandler<Node>(value);
            }
            remove
            {
                CollectionChanging -= new NotifyCollectionChangingEventHandler<Node>(value);
            }
        }

        public event NotifyCollectionChangedEventHandler<Node> CollectionChanged;
        event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
        {
            add
            {
                CollectionChanged += new NotifyCollectionChangedEventHandler<Node>(value);
            }
            remove
            {
                CollectionChanged -= new NotifyCollectionChangedEventHandler<Node>(value);
            }
        }

        public abstract CancellationToken CancellationToken { get; }

        #region Synchronization

        public abstract bool Barrier();
        public abstract bool Barrier(uint millisecondsTimeout);
        public abstract bool Barrier(TimeSpan timeout);

        public abstract bool MemoryBarrier();

        #endregion

        #region Reduction

        public abstract decimal Max(decimal item);
        public abstract float Max(float item);
        public abstract double Max(double item);
        public abstract sbyte Max(sbyte item);
        public abstract short Max(short item);
        public abstract int Max(int item);
        public abstract long Max(long item);
        public abstract byte Max(byte item);
        public abstract ushort Max(ushort item);
        public abstract uint Max(uint item);
        public abstract ulong Max(ulong item);
        public abstract T Max<T>(T item);

        public abstract decimal Min(decimal item);
        public abstract float Min(float item);
        public abstract double Min(double item);
        public abstract sbyte Min(sbyte item);
        public abstract short Min(short item);
        public abstract int Min(int item);
        public abstract long Min(long item);
        public abstract byte Min(byte item);
        public abstract ushort Min(ushort item);
        public abstract uint Min(uint item);
        public abstract ulong Min(ulong item);
        public abstract T Min<T>(T item);

        public abstract decimal Sum(decimal item);
        public abstract float Sum(float item);
        public abstract double Sum(double item);
        public abstract sbyte Sum(sbyte item);
        public abstract short Sum(short item);
        public abstract int Sum(int item);
        public abstract long Sum(long item);
        public abstract byte Sum(byte item);
        public abstract ushort Sum(ushort item);
        public abstract uint Sum(uint item);
        public abstract ulong Sum(ulong item);

        public abstract bool All(bool item);
        public abstract bool Any(bool item);
        public abstract INDEX Sum(bool item);

        public abstract uint And(uint item);
        public abstract uint ExclusiveOr(uint item);
        public abstract uint Or(uint item);
        public abstract ulong And(ulong item);
        public abstract ulong ExclusiveOr(ulong item);
        public abstract ulong Or(ulong item);

        public abstract TElement Reduce<TElement>(TElement item, Expression<Func<TElement, TElement, TElement>> reduction);

        #endregion

        #region Gathering

        public abstract Library.Collections.Generic.IVectorMultidimensionalReadOnly<TElement> AllGather<TElement>(TElement item);
        //public abstract Array AllGatherToArray<TElement>(TElement item);

        //public abstract Library.Collections.Generic.IReadOnlyVector<TElement> AllGatherFlattenToVector<TElement>(IEnumerable<TElement> item);
        //public abstract Library.Collections.Generic.IReadOnlyVector<TElement> AllGatherFlattenToVector<TElement>(Library.Collections.Generic.IReadOnlyVector<TElement> item);
        //public abstract TElement[] AllGatherFlattenToArray<TElement>(IEnumerable<TElement> item);

        #endregion

        #region Composition

        public abstract IDistributed<TElement> Compose<TElement>(TElement item);
        public abstract IDistributed<TElement> Compose<TElement>(TElement item, bool include);
        public abstract IDistributed<TElement> Compose<TElement>(TElement item, Expression<Func<TElement, bool>> predicate);

        public abstract IDistributedEnumerable Compose(IEnumerable enumerable);
        public abstract IDistributedEnumerable<TElement> Compose<TElement>(IEnumerable<TElement> enumerable);

        public abstract IDistributedQueryable Compose(System.Linq.IQueryable queryable);
        public abstract IDistributedQueryable<TElement> Compose<TElement>(System.Linq.IQueryable<TElement> queryable);

        public abstract IDistributedEnumerable<TElement> Compose<TElement>(IEnumerable<TElement> enumerable, Expression<Func<TElement, bool>> predicate);
        public abstract IDistributedEnumerable<TElement> Compose<TElement>(IEnumerable<TElement> enumerable, Expression<Func<TElement, INDEX, bool>> predicate);

        public abstract IDistributedQueryable<TElement> Compose<TElement>(System.Linq.IQueryable<TElement> enumerable, Expression<Func<TElement, bool>> predicate);
        public abstract IDistributedQueryable<TElement> Compose<TElement>(System.Linq.IQueryable<TElement> enumerable, Expression<Func<TElement, INDEX, bool>> predicate);

        public abstract IDistributedOrderedEnumerable<TElement> Compose<TElement, TKey>(IOrderedEnumerable<TElement> orderedEnumerable, Expression<Func<TElement, TKey>> keySelector);
        public abstract IDistributedOrderedEnumerable<TElement> Compose<TElement, TKey>(IOrderedEnumerable<TElement> orderedEnumerable, Expression<Func<TElement, TKey>> keySelector, bool descending);

        public abstract IDistributedOrderedQueryable<TElement> Compose<TElement, TKey>(IOrderedQueryable<TElement> orderedEnumerable, Expression<Func<TElement, TKey>> keySelector);
        public abstract IDistributedOrderedQueryable<TElement> Compose<TElement, TKey>(IOrderedQueryable<TElement> orderedEnumerable, Expression<Func<TElement, TKey>> keySelector, bool descending);

        public abstract IDistributedEnumerable<TElement> ComposeDistinct<TElement>(IEnumerable<TElement> enumerable);

        public abstract IDistributedEnumerable<System.Linq.IGrouping<TKey, TSource>> Compose<TKey, TSource>(IEnumerable<System.Linq.IGrouping<TKey, TSource>> grouping, Expression<Func<TSource, TKey>> keySelector);
        public abstract IDistributedEnumerable<TResult> Compose<TKey, TSource, TResult>(IEnumerable<System.Linq.IGrouping<TKey, TSource>> grouping, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TKey, IEnumerable<TSource>, TResult>> resultSelector);
        public abstract IDistributedEnumerable<System.Linq.IGrouping<TKey, TElement>> Compose<TKey, TSource, TElement>(IEnumerable<System.Linq.IGrouping<TKey, TSource>> grouping, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector);
        public abstract IDistributedEnumerable<TResult> Compose<TKey, TSource, TElement, TResult>(IEnumerable<System.Linq.IGrouping<TKey, TSource>> grouping, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector, Expression<Func<TKey, IEnumerable<TElement>, TResult>> resultSelector);

        #endregion

        #region Collective Communication

        public abstract Library.Collections.Generic.IVectorMultidimensionalReadOnly<T> AllToAll<T>(Library.Collections.Generic.IVectorMultidimensionalReadOnly<T> data);

        #endregion

        #region Loop Optimization

        // resembles System.Threading.Tasks.Parallel, loop state data can be in Context.Current
        public abstract void For(INDEX fromInclusive, INDEX toExclusive, Action<INDEX> body);
        public abstract void For<TLocal>(INDEX fromInclusive, INDEX toExclusive, Func<TLocal> localInit, Func<INDEX, TLocal, TLocal> body, Action<TLocal> localFinally);

        // two-dimensional
        public abstract void For(INDEX fromInclusive1, INDEX toExclusive1, INDEX fromInclusive2, INDEX toExclusive2, Action<INDEX, INDEX> body);
        public abstract void For<TLocal>(INDEX fromInclusive1, INDEX toExclusive1, INDEX fromInclusive2, INDEX toExclusive2, Func<TLocal> localInit, Func<INDEX, INDEX, TLocal, TLocal> body, Action<TLocal> localFinally);

        // three-dimensional
        public abstract void For(INDEX fromInclusive1, INDEX toExclusive1, INDEX fromInclusive2, INDEX toExclusive2, INDEX fromInclusive3, INDEX toExclusive3, Action<INDEX, INDEX, INDEX> body);
        public abstract void For<TLocal>(INDEX fromInclusive1, INDEX toExclusive1, INDEX fromInclusive2, INDEX toExclusive2, INDEX fromInclusive3, INDEX toExclusive3, Func<TLocal> localInit, Func<INDEX, INDEX, INDEX, TLocal, TLocal> body, Action<TLocal> localFinally);

        // ...

        #endregion

        public abstract Context GroupBy<T>(T value);
        public abstract Context GroupBy<T>(T value, CancellationToken cancellationToken);

        public abstract Context Neighborhood();
        public abstract Context Neighborhood(CancellationToken cancellationToken);

        public abstract Context Subspace(params bool[] dimensions);
        public abstract Context Subspace(bool[] dimensions, CancellationToken cancellationToken);

        public abstract Context Tile(params INDEX[] extents);
        public abstract Context Tile(INDEX[] extents, CancellationToken cancellationToken);

        public abstract void BeginCriticalSection();
        public abstract void EndCriticalSection();

        //public abstract void BeginFixedTopologySection();
        //public abstract void EndFixedTopologySection();

        public abstract void Dispose();
    }
}